from os.path import dirname, join
from urllib.parse import urlparse
from qtpy import QtWidgets, QtGui
from qtpy import QtCore
from qtpy.QtCore import QModelIndex, QUrl, Qt, QStringListModel, Signal as pyqtSignal
from qtpy import QtWebEngineWidgets
from bs4 import BeautifulSoup
from scrapy.selector import Selector
import tomli_w
from .html_itemmodel import HtmlDomModel, HtmlItem, ScraperItemModel
from .html_content import validate_url_format, get_html_data, get_html, export_csv
from .ui_scraper import Ui_Form
from .ui_starturls import Ui_Dialog


def get_domain(url: str) -> str:
    """从URL中提取完整域名"""
    if not url.startswith(('http://', 'https://')):
        url = f'http://{url}'  # 添加默认协议头
    
    try:
        parsed = urlparse(url)
        if parsed.netloc:
            return parsed.netloc.split(':')[0]  # 去除端口号
        return ''
    except:
        return ''


class StartUrls(QtWidgets.QDialog):
    list_data = pyqtSignal(list)

    def __init__(self, parent=None):
        super(StartUrls, self).__init__(parent)
        self.ui = Ui_Dialog()
        self.ui.setupUi(self)
        self.model = QStringListModel()
        self.ui.listView.setModel(self.model)

    def on_pushButton_add_pressed(self):
        url = self.ui.lineEdit_url.text().strip()
        self.model.insertRow(self.model.rowCount())
        self.model.setData(self.model.index(self.model.rowCount()-1), url)
        self.ui.lineEdit_url.clear()

    def on_pushButton_del_pressed(self):
        self.model.removeRow(self.ui.listView.currentIndex().row())

    def closeEvent(self, a0: QtGui.QCloseEvent) -> None:
        data = []
        for i in range(self.model.rowCount()):
            data.append(self.model.data(self.model.index(i), Qt.DisplayRole))
        self.list_data.emit(data)

class EditableDelegate(QtWidgets.QItemDelegate):
    def createEditor(self, parent, option, index):
        editor = QtWidgets.QLineEdit(parent)
        editor.setReadOnly(True)
        return editor

class Scraper(QtWidgets.QWidget):

    def __init__(self, parent=None):
        super().__init__(parent)
        self.ui = Ui_Form()
        self.ui.setupUi(self)
        self.setWindowTitle('爬虫辅助工具scraper')
        self.ui.radioButton_css.setChecked(True)
        self.ui.comboBox_url.setCurrentText("https://top.baidu.com/board?tab=realtime")
        self.ui.listView_result.setContentsMargins(2, 2, 2, 2)
        self.ui.listView_result.setResizeMode(QtWidgets.QListView.Adjust)
        self.ui.listView_result.setContextMenuPolicy(Qt.CustomContextMenu)
        self.ui.listView_result.customContextMenuRequested.connect(self.show_context_menu)
        self.ui.listView_result.setItemDelegate(EditableDelegate(self.ui.listView_result))

        self.item_model = ScraperItemModel()
        self.ui.tableView.setModel(self.item_model)
        # self.ui.plainTextEdit_history.setLineWrapMode(QtWidgets.QPlainTextEdit.NoWrap)
        
        self.ui.lineEdit_conditon.returnPressed.connect(self.on_pushButton_query_released)

        try:
            with open(join(dirname(__file__), 'help.txt'), 'r', encoding='utf-8') as f:
                self.ui.textBrowser_help.setPlainText(''.join(f.readlines()))
        except Exception as e:
            print(type(e), e)

        self.html: HtmlDomModel = None
        self.url = ''
        self.selector: Selector = None
        self.ui.pushButton_view.setEnabled(False)

    def on_pushButton_get_released(self):
        self.ui.pushButton_get.setEnabled(False)
        url = self.ui.comboBox_url.currentText().strip()
        if url != '':
            if 'http://' not in url and 'https://' not in url:
                url = 'https://' + url
                # self.ui.lineEdit_url.setText(url)
            if not validate_url_format(url):
                QtWidgets.QMessageBox.critical(self,
                                               '错误',
                                               '请输入正确网址！')
                # self.ui.lineEdit_url.clear()
                return
            self.ui.textBrowser_html.setPlainText('正在获取并解析中...')
            self.htmlstr = get_html(url, True)
            self.html = HtmlDomModel(self.htmlstr)
            self.url = url
            self.ui.textBrowser_html.setPlainText('获取成功，正在设置...')
            self.ui.treeView_node.setModel(self.html)
            self.set_formatted_html_str(self.html.htmlstr)
            self.selector = Selector(text=self.html.htmlstr)
            self.ui.pushButton_get.setEnabled(True)
            self.ui.pushButton_view.setEnabled(True)
            self.ui.tabWidget.setCurrentIndex(0)

    def on_treeView_node_clicked(self, index: QModelIndex):
        self.ui.treeView_node.resizeColumnToContents(0)
        if self.html is None:
            return
        item: HtmlItem = self.html.itemFromIndex(index)
        self.ui.lineEdit_xpath.setText(self.html.get_item_xpath(item))
        self.ui.lineEdit_css.setText(
            f"{item.tag}[class=\"{item.attrib.get('class', '')}\"]" if item.attrib.get('class', '') else item.tag)
        self.ui.plainTextEdit_attr.setPlainText(str(item.attrib))
        self.ui.textEdit_text.setPlainText(item.tdata)

    def on_pushButton_query_released(self):
        text = self.ui.lineEdit_conditon.text().strip()
        if text == '':
            return
        if self.url == '':
            QtWidgets.QMessageBox.critical(self,
                                           '错误',
                                           '请输入网址并获取后在查询')
            self.ui.lineEdit_conditon.clear()
            return
        re = None
        mod = ''
        try:
            if self.ui.radioButton_css.isChecked():
                re = self.selector.css(text)
                mod = 'css'
            elif self.ui.radioButton_xpath.isChecked():
                re = self.selector.xpath(text)
                mod = 'xpath'
            elif self.ui.radioButton_regex.isChecked():
                re = self.selector.re(text)
                mod = 'regex'
            else:
                QtWidgets.QMessageBox.critical(self,
                                               '错误',
                                               '请选择查询方式')
                return
            if not re:
                QtWidgets.QMessageBox.critical(self,
                                               '错误',
                                               '未查询到数据')
                return
            else:
                if mod !='regex' and re:
                    re = [i.get() for i in re]
                model = QtCore.QStringListModel()
                model.setStringList(re)
                self.ui.listView_result.setModel(model)
                self.ui.lineEdit_conditon.clear()
                self.ui.tabWidget.setCurrentIndex(2)
                # 判断text在self.item_model的第3列中是否存在，如果存在则不添加，如果不存在则添加
                if len(re) > 0  and self.item_model.findItems(text, Qt.MatchExactly, 2) == []:
                    self.item_model.add_row('item' + str(self.item_model.rowCount()), mod, text)
        except Exception as e:
            QtWidgets.QMessageBox.critical(self,
                                           '错误',
                                           '请检查查询语法!\n错误信息：%s\n%s'
                                           % (str(type(e)), str(e)))
            # self.ui.lineEdit_conditon.clear()
            return

        
    
    def on_tableView_clicked(self, index: QModelIndex):
        self.ui.tableView.resizeColumnToContents(0)
        if self.item_model is None or self.item_model.rowCount() == 0:
            return
        row = index.row()
        item1 = self.item_model.item(row, 1)
        if item1:
            if item1.text() == 'css':
                self.ui.radioButton_css.setChecked(True)
            elif item1.text() == 'xpath':
                self.ui.radioButton_xpath.setChecked(True)
            elif item1.text() =='regex':
                self.ui.radioButton_regex.setChecked(True)
        else:
            return
        item2 = self.item_model.item(row, 2)
        if item2:
            self.ui.lineEdit_conditon.setText(item2.text())
        else:
            return

    def on_pushButton_view_released(self):
        if self.url == '':
            return
        self.b = QtWebEngineWidgets.QWebEngineView()
        self.b.setWindowTitle('网页查看')
        self.b.setContextMenuPolicy(Qt.NoContextMenu)
        self.b.setUrl(QUrl(self.url))
        self.b.show()

    def on_pushButton_del_pressed(self):
        index = self.ui.tableView.currentIndex()
        if index:
            self.item_model.removeRow(index.row())

   
    def on_pushButton_pressed(self):
        self.manurl = StartUrls()
        self.manurl.list_data.connect(self.ui.comboBox_url.addItems)

        self.manurl.show()

    def show_context_menu(self, pos):
        menu = QtWidgets.QMenu()
        copy_action = menu.addAction("复制")
        action = menu.exec_(self.ui.listView_result.mapToGlobal(pos))
        if action == copy_action:
            index = self.ui.listView_result.currentIndex()
            if index.isValid():
                clipboard = QtWidgets.QApplication.clipboard()
                clipboard.setText(index.data())
                
    def on_pushButton_copy_pressed(self):
        # 获取剪贴板内容
        clipboard = QtWidgets.QApplication.clipboard()
        clipboard.setText(self.ui.textBrowser_html.toPlainText())

    def set_formatted_html_str(self, html_str):
        # 格式化HTML字符串

        # 使用BeautifulSoup格式化
        soup = BeautifulSoup(html_str, 'html.parser')
        formatted_html = soup.prettify()  # 这会自动添加缩进和换行

        # 在界面展示（可替换你现有的textBrowser_html设置）
        self.ui.textBrowser_html.setPlainText(formatted_html)

        # 设置文本框的滚动条位置
        self.ui.textBrowser_html.verticalScrollBar().setValue(0)

    def on_pushButton_paste_pressed(self):
        # 获取剪贴板内容
        clipboard = QtWidgets.QApplication.clipboard()
        self.set_formatted_html_str(clipboard.text())

    def on_pushButton_clear_pressed(self):
        self.ui.textBrowser_html.clear()

    def on_pushButton_help_pressed(self):
        self.ui.tabWidget.setCurrentIndex(3)

    def on_pushButton_export_pressed(self):
        if self.item_model.rowCount() == 0:
            return
        # 将模型中的数据导出到整理为字典的列表
        data = []
        for row in range(self.item_model.rowCount()):
            item_data = {}
            for col in range(self.item_model.columnCount()):
                index = self.item_model.index(row, col)
                item_data[self.item_model.headerData(col, Qt.Horizontal)] = index.data()
            data.append(item_data)

        html_data = get_html_data(data, self.html.htmlstr)
        # html_data保存到CSV文件, 并提示保存成功
        file_path= QtWidgets.QFileDialog.getExistingDirectory(self, "保存目录", ".", QtWidgets.QFileDialog.ShowDirsOnly)
        if html_data and file_path and export_csv(html_data, file_path):
            QtWidgets.QMessageBox.information(self, "保存成功", "数据已成功保存到CSV文件。")
    
    def on_pushButton_download_pressed(self):
        if self.item_model.rowCount() == 0:
            return
        # 获取第一列的所有数据
        field_order = []
        rule_str = {}
        for row in range(self.item_model.rowCount()):
            name = self.item_model.item(row, 0).text()
            field_order.append(name)
            rule_str[name]= self.item_model.item(row, 1).text() + "##" + self.item_model.item(row, 2).text()
        toml_data = {
            'name':'data',
            'allowed_domains': [get_domain(self.url)] if self.url else [],
            'start_urls': [self.url] if self.url else [],
            'next_urls' : [''],
            "field_order" :field_order,
            "item_model": {i:'' for i in field_order},
            "item_model_declare": {i: 'str' for i in field_order},
            'parse_data': rule_str
        }
        file_path= QtWidgets.QFileDialog.getSaveFileName(self, "保存文件", ".", "Scrapy Project Config Files (*.toml)")[0]
        if file_path:
            try:
                with open(file_path, 'wb') as f:                
                    tomli_w.dump(toml_data, f)
                QtWidgets.QMessageBox.information(self, "保存成功", "数据已成功保存到TOML文件。")
            except Exception as e:
                QtWidgets.QMessageBox.critical(self, "保存失败", f"文件保存失败：{str(e)}")

def scraper_main(args=[]):
    if args is None:
        args = []
    app = QtWidgets.QApplication(args)
    app.setStyle(QtWidgets.QStyleFactory.create("Fusion"))
    s = Scraper()
    s.show()
    return app.exec_()